<?php

namespace App\Services;

use App\Models\Project;
use App\Repositories\Contract\CellRepository;
use App\Repositories\Contract\ColumnRepository;
use App\Repositories\Contract\ProjectRepository;
use App\Repositories\Contract\RowRepository;
use App\Repositories\Criteria\MineCriteria;
use App\Services\Contract\ProjectServiceInterface;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Collection;
use App\Table\Constant\Project as CProject;

class ProjectService extends AbstractService implements ProjectServiceInterface {

    /**
     * @var ProjectRepository
     */
    protected $project;

    /**
     * @var ColumnRepository
     */
    protected $column;

    /**
     * @var RowRepository
     */
    protected $row;

    /**
     * @var CellRepository
     */
    protected $cell;

    public function __construct(ProjectRepository $project,
                                ColumnRepository $column,
                                RowRepository $row,
                                CellRepository $cell)
    {
        $this->project = $project;
        $this->column = $column;
        $this->row = $row;
        $this->cell = $cell;
        $this->project->pushCriteria(MineCriteria::class);
    }

    /**
     * @inheritDoc
     */
    public function rename(int $id, $title): bool
    {
        try{
            $this->project->scopeQuery(function ($query){
                return $query->where(['user_id' => \Auth::id()]);
            })->update(['title' => $title], $id);
        }catch (ModelNotFoundException $e) {
            $this->error = '项目未找到';
            return false;
        }
        return true;

    }

    /**
     * @inheritDoc
     */
    public function create(int $templateId, string $title): ?Project
    {
        try{
            $templateProject = $this->project->skipCriteria()->find($templateId);
        }catch (ModelNotFoundException $e) {
            $this->error = '项目未找到';
            return null;
        }


        if($templateProject->user_id != \Auth::id() && ($templateProject->template != CProject::TEMPLATE_YES || $templateProject->system != CProject::SYSTEM_YES)) {
            $this->error = '不是本人的项目或者系统模板不能创建';
            return null;
        }

        $templateProject->title = $title;

        $columns = $templateProject->columns;

        $columnRoot = $this->makeLinkedList($columns);

        $rows = $templateProject->rows;

        $rowRoot = $this->makeLinkedList($rows);

        $cells = $templateProject->cells;

        \DB::beginTransaction();

        try{

            $newProject = $this->project->copy($templateProject, \Auth::id());

            $column_prev_id = 0;
            $curr_column = $columnRoot;
            $columnMap = [];

            do{
                $newColumn = $this->column->copy($curr_column, $newProject->id, $column_prev_id);
                $columnMap[$curr_column->id] = $newColumn->id;
                $column_prev_id = $newColumn->id;
            }while(!is_null($curr_column = $curr_column->next));

            $row_prev_id = 0;
            $curr_row = $rowRoot;
            $rowMap = [];

            do {
                $newRow = $this->row->copy($curr_row, $newProject->id, $row_prev_id);
                $rowMap[$curr_row->id] = $newRow->id;
                $row_prev_id = $newRow->id;
            }while(!is_null($curr_row = $curr_row->next));


            foreach ($cells as $cell) {
                $this->cell->copy($cell, $newProject->id, $columnMap[$cell->column_id], $rowMap[$cell->row_id]);
            }

            \DB::commit();

            return $newProject;

        }catch (\Exception $e) {
            \DB::rollBack();
            throw $e;
        }
    }

    protected function makeLinkedList(Collection $collection) {
        $map = [];
        $root = null;
        foreach ($collection as $item) {
            $map[$item->id] = $item;
        }

        foreach ($collection as $item) {

            $prevId = $item->prev_id;

            if($prevId == 0) {
                $root = $item;
                continue;
            }

            if(isset($map[$prevId])) {
                $map[$prevId]->next = $item;
            }
        }

        return $root;
    }

    /**
     * @inheritDoc
     */
    public function copy(int $id): ?Project
    {
        try{
            $object = $this->project->find($id);
        }catch (ModelNotFoundException $e) {
            $this->error = '项目未找到';
            return null;
        }

        return $this->create($id, $object->title . '-副本');
    }

    /**
     * @inheritDoc
     */
    public function trash(int $id): bool
    {
        try{
            $this->project->scopeQuery(function ($query){
                return $query->where(['user_id' => \Auth::id(), 'status' => CProject::STATUS_NORMAL]);
            })->update(['status' => CProject::STATUS_TRASH], $id);
        }catch (ModelNotFoundException $e) {
            $this->error = '项目未找到';
            return false;
        }
        return true;
    }

    /**
     * @inheritDoc
     */
    public function restore(int $id): bool
    {
        try{
            $this->project->scopeQuery(function ($query){
                return $query->where(['user_id' => \Auth::id(), 'status' => CProject::STATUS_TRASH]);
            })->update(['status' => CProject::STATUS_NORMAL], $id);
        }catch (ModelNotFoundException $e) {
            $this->error = '项目未找到';
            return false;
        }
        return true;
    }

    function paginate(array $query, int $pageSize)
    {
        return $this->project->scopeQuery(function ($builder) use ($query) {
            return $builder->where($query);
        })->orderBy('created_at', 'DESC')->paginate($pageSize);
    }

}